/*
 * The MIT License
 *
 * Copyright 2012 Anonymous.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
package pentomino.logic;

/**
 *
 * Represents the game field logic.
 */
public class Playground {
    
    // List of the game field types:
    public static final int FIELD3X20 = 1;
    public static final int FIELD4X15 = 2;
    public static final int FIELD5X12 = 4;
    public static final int FIELD6X10 = 8;
    
    private char[][] field = null; // A game field data.
    private int width; // A field width.
    private int height; // A field height.
    private int type; // A field type.
    
    /*============== Constructors ==============*/
    
    /**
     * Creates an instance of the Playground class by default.
     */
    public Playground()
    {
        this(0);
    }
    
    /**
     * Creates an instance of the Playground class by the specified type.
     */
    public Playground(int type)
    {
        switch (type)
        {
            case FIELD3X20:
                width = 20;
                height = 3;
                break;
            case FIELD4X15:
                width = 15;
                height = 4;
                break;
            case FIELD5X12:
                width = 12;
                height = 5;
                break;
            case FIELD6X10:
                width = 10;
                height = 6;
                break;
            default:
                width = 15;
                height = 4;
        }
        
        field = new char[width][height];
        this.type = type;
        refresh();
    }
    
    /**
     * Creates an instance of the Playground class by the specified width
     * and height values which indicates the game field dimentions. 
     * 
     * @param width a game field width value
     * @param height a game field height value
     */
    public Playground(int width, int height)
    {
        if (width < 1 || height < 1)
            throw new IllegalArgumentException("The 'width' and 'height' arguments should be a natural numbers.");
        
        this.width = width;
        this.height = height;
        
        this.field = new char[width][height];
        type = 0;
        refresh();
    }
    
    /*============== Member access ==============*/
    
    /**
     * Gets a game field size.
     * 
     * @return the current game field width value
     */
    public int getWidth()
    {
        return width;
    }
    
    /**
     * Gets a game field size.
     * 
     * @return the current game field height value
     */
    public int getHeight()
    {
        return height;
    }
    
    /**
     * Gets a game field type.
     * 
     * @return the current game field type
     */
    public int getType()
    {
        return type;
    }
    
    /*============== Methods ==============*/
    
    /**
     * Clears the game field data.
     */
    public final void refresh()
    {
        if (field != null)
        {
            for (int j = 0; j < height; j++)
                for (int i = 0; i < width; i++)
                    this.field[i][j] = '0';
        }
    }
    
    /**
     * Gets the figure type at the specified position on the game field.
     * 
     * @param x x-coord of the place
     * @param y y-coord of the place
     * @return the special figure identifier (character) or '0' if empty
     */
    public char figureAt(int x, int y)
    {
        return field[x][y];
    }
    
    /**
     * Tries to insert the figure in the specified position.
     * 
     * @param source a game figure
     * @param x x-coord on the game field
     * @param y y-coord on the game field
     * @return the possibility for insert action
     */
    public boolean tryInsert(Figure source, int x, int y)
    {
        if (x < 0 || y < 0 || x >= width || y >= height)
            return false;
        
        int dx = x - source.getColumns() / 2;
        int dy = y - source.getRows() / 2;
        int mx = source.getColumns();
        int my = source.getRows();
        
        for (int j = 0; j < my; j++)
        {
            for (int i = 0; i < mx; i++)
            {
                byte b = source.bitAt(j, i);
                
                if (dx + i < 0 || dy + j < 0 || dx + i >= width || dy + j >= height)
                {
                    if (b == 0)
                        continue;
                    else
                        return false;
                }
                
                if (b == 1)
                {
                    if (field[dx + i][dy + j] != '0')
                        return false;
                    else
                        continue;
                }
            }
        }
        
        return true;
    }
    
    /**
     * Places the figure at the specified position on the game field.
     * 
     * @param source a game figure
     * @param x x-coord on the game field
     * @param y y-coord on the game field
     * @return true if the figure was successfully added and false in another case
     */
    public boolean insert(Figure source, int x, int y)
    {
        boolean itPossible = tryInsert(source, x, y);
        
        if (itPossible)
        {
            int dx = x - source.getColumns() / 2;
            int dy = y - source.getRows() / 2;
            int mx = source.getColumns();
            int my = source.getRows();

            for (int j = 0; j < my; j++)
            {
                for (int i = 0; i < mx; i++)
                {
                    byte b = source.bitAt(j, i);

                    if (b == 1)
                    {
                        field[dx + i][dy + j] = source.getType();
                    }
                }
            }
        }
        
        return itPossible;
    }
    
    /**
     * Removes the figure from the game field.
     * 
     * @param source a game figure
     */
    public void remove(Figure source)
    {
        remove(source.getType());
    }
    
    /**
     * Removes the figure from the game field.
     * 
     * @param source a game figure type 
     */
    public void remove(char source)
    {
        for (int y = 0; y < height; y++)
            for (int x = 0; x < width; x++)
                if (field[x][y] == source)
                    field[x][y] = '0';
    }
    
    /**
     * Indicates when the game field is full.
     * 
     * @return boolean value that indicates a full-up state.
     */
    public boolean isFull()
    {
        for (int y = 0; y < height; y++)
            for (int x = 0; x < width; x++)
                if (field[x][y] == '0')
                    return false;
        
        return true;
    }
    
    /**
     * Converts the game field into a string value for print (output).
     */
    @Override
    public String toString()
    {
        StringBuilder sb = new StringBuilder();
        
        for (int y = 0; y < height; y++)
        {
            for (int x = 0; x < width; x++)
                sb.append(field[x][y]).append(' ');
            
            sb.append('\n');
        }
        
        return sb.toString();
    }
}
